브로드캐스트 리시버 결함
브로드캐스트 리시버란?
안드로이드 기기에서 이벤트가 발생하면 브로드캐스트 신호를 전송하는데 그 신호를 처리하는게 브로드캐스트 리시버이다.
신호를 받아 처리하기 위해선 AndroidManifest.xml에 설정을 해주어야 한다. 설정을 통해 작동할 작업을 정의 할 수 있다.
브로드캐스트 리시버의 사용 예로는 배터리 상태, 메세지 송.수신 등이 있다.
취약점 진단
취약점 진단을 위해 AndroidManifest.xml을 보면 <receiver>의 exported가 True로 이루어져 있는 것을 볼 수 있다. 따라서 모든 곳에서 엑세스가 가능하여 외부 접근이 가능해 취약하다.
코드를 살펴보면 theBroadcast가 생성 되면 MyBroadCastReceiver메서드를 호출하는 것을 알 수 있다.
<receiver android:name="com.android.insecurebankv2.MyBroadCastReceiver" android:exported="true"> <intent-filter> <action android:name="theBroadcast"/> </intent-filter> </receiver>
MyBroadCastReceiver의 코드를 살펴보면 전화번호와 비밀번호를 변경하는 기능을 하는 것을 볼 수 있다.
변경 될 전화번호와 비밀번호는 intent의 phonenumber, newpass를 통해 가져온다. 만일 intent에 phonenumber값이 존재하지 않으면 “Phone number is null”을 호출한다.
따라서 임의로 theBroadcast를 호출해 비밀번호와 전화번호를 변경 할 수 있다.
package com.android.insecurebankv2; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.telephony.SmsManager; import android.util.Base64; /* loaded from: classes.dex */ public class MyBroadCastReceiver extends BroadcastReceiver { public static final String MYPREFS = "mySharedPreferences"; String usernameBase64ByteString; @Override // android.content.BroadcastReceiver public void onReceive(Context context, Intent intent) { String phn = intent.getStringExtra("phonenumber"); String newpass = intent.getStringExtra("newpass"); if (phn != null) { try { SharedPreferences settings = context.getSharedPreferences("mySharedPreferences", 1); String username = settings.getString("EncryptedUsername", null); byte[] usernameBase64Byte = Base64.decode(username, 0); this.usernameBase64ByteString = new String(usernameBase64Byte, "UTF-8"); String password = settings.getString("superSecurePassword", null); CryptoClass crypt = new CryptoClass(); String decryptedPassword = crypt.aesDeccryptedString(password); String textPhoneno = phn.toString(); String textMessage = "Updated Password from: " + decryptedPassword + " to: " + newpass; SmsManager smsManager = SmsManager.getDefault(); System.out.println("For the changepassword - phonenumber: " + textPhoneno + " password is: " + textMessage); smsManager.sendTextMessage(textPhoneno, null, textMessage, null, null); return; } catch (Exception e) { e.printStackTrace(); return; } } System.out.println("Phone number is null"); } }
ADB를 이용한 브로드캐스트 생성
ADB에서는 am(activity manager)을 통해 브로드캐스트를 생성 가능하다.
am을 통해 broadcast를 생성해보고 logcat을 통해 확인해보았다.
am broadcast -a theBroadcast -n com.android.insecurebankv2/.MyBroadCastReceiver
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F0de85482-c105-4bc9-8ce2-427dab62a0c0%2Fe9e5e980-a94f-45cf-b736-f1651557f2c6%2FUntitled.png?table=block&id=cbee8040-6b57-4552-b7fe-ad045827537a&cache=v2)
![logcat](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F0de85482-c105-4bc9-8ce2-427dab62a0c0%2Fda3de7d4-0507-4c35-a426-b851bdce7f98%2FUntitled.png?table=block&id=fd57714f-6554-4077-b048-e47df1ddeb07&cache=v2)
intent를 통해 아무런 값도 전송하지 않았기 때문에 Phone number is null을 메세지를 출력한다.
am에 —es 옵션을 사용하게 되면 변수를 전송 할 수 있다.
phonenumber와 newpass를 포함하여 전송해보았다.
am broadcast -a theBroadcast -n com.android.insecurebankv2/.MyBroadCastReceiver --es phonenumber 5555 --es newpass test
비밀번호가 변경되진 않았지만 기존 비밀번호가 평문으로 표기되는 것을 알 수 있다.
![logcat](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F0de85482-c105-4bc9-8ce2-427dab62a0c0%2F5c06f010-3208-42e1-aa58-d9a34e0464be%2FUntitled.png?table=block&id=fe278394-273d-437a-a548-7119f4ffdf9c&cache=v2)
Drozer를 통한 브로드캐스트 생성
드로저는 위 주소에서 다운받아 사용가능하다.
해당 명령어를 통해 브로드캐스트 리시버의 정보를 확인 할 수 있다. 실행 결과 Permission은 null로 지정되어 있는 MyBroadCastReceiver를 확인 할 수 있다.
dz> run app.broadcast.info -a com.android.insecurebankv2
![drozer 코드 실행화면](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F0de85482-c105-4bc9-8ce2-427dab62a0c0%2Ff20b3a60-5801-48db-bee9-7db3383a22a5%2FUntitled.png?table=block&id=fb8b6558-60b2-4331-ade2-25c87ce0ef45&cache=v2)
app.boradcast.send를 사용하면 브로드캐스트를 생성 할 수 있다. drozer는 —extra를 통해 변수를 추가 할 수 있다.
아래 코드를 통해 기존 암호를 평문으로 얻을 수 있다.
dz> run app.broadcast.send --component com.android.insecurebankv2 com.android.insecurebankv2.MyBroadCastReceiver --extra string phonenumber 4444 --extra string newpass drozetest
ADB와 동일하게 logcat을 통해 비밀번호를 확인이 가능하다.
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F0de85482-c105-4bc9-8ce2-427dab62a0c0%2F1f87f737-75d9-45f8-bff4-358347603e93%2FUntitled.png?table=block&id=da0de802-1c88-45f6-9234-e34cd1b6da78&cache=v2)
대응방안
해당 취약점은 receiver의 export가 True로 되어 있기 때문에 외부 접근이 가능해 취약했다.
따라서 export 값을 아래 코드와 같이 False로 변경하게 되면 해당 앱의 UID만이 receiver에 대한 권한이 생기게 되어 더 이상 취약하지 않다.
<receiver android:name="com.android.insecurebankv2.MyBroadCastReceiver" android:exported="false"> <intent-filter> <action android:name="theBroadcast"/> </intent-filter> </receiver>
다른 방법으로는 receiver에 별도의 Permission을 부여하는 것이다.